#ifndef GAMEENGINE_GEOMETRY_TRGL3DFAST_HPP
#define GAMEENGINE_GEOMETRY_TRGL3DFAST_HPP

#ifdef GAMEENGINE_NAMESPACES
namespace GameEngine {
#endif

//=================================================================================
// Implementation of the Trgl3dFast class

//! Constructor from three points
/*!
 @param p1 The first point
 @param p2 The second point
 @param p3 The third point
 @note Class T should have a default constructor
 @note Class T should have an assignment operator
 */
template <class T>
Trgl3dFast<T>::Trgl3dFast(const Point3d<T>& p1, const Point3d<T>& p2, const Point3d<T>& p3) : p1_(p1), p2_(p2), p3_(p3) {}

//! Destructor
/*!
 @note The destructor of class T should be public
 */
template <class T>
Trgl3dFast<T>::~Trgl3dFast() {}

//! Accessor to the first point (read only)
/*!
 @return A constant reference to the first point
 */
template <class T>
const Point3d<T>& Trgl3dFast<T>::p1() const {
	return p1_;
}

//! Accessor to the second point (read only)
/*!
 @return A constant reference to the second point
 */
template <class T>
const Point3d<T>& Trgl3dFast<T>::p2() const {
	return p2_;
}

//! Accessor to the third point (read only)
/*!
 @return A constant reference to the third point
 */
template <class T>
const Point3d<T>& Trgl3dFast<T>::p3() const {
	return p3_;
}

//! Compute the U axis of the triangle
/*!
 @return The U axis of the triangle
 */
template <class T>
Vector3d<T> Trgl3dFast<T>::u_axis() const {
	return Vector3d<T>(p1_, p2_);
}

//! Compute the V axis of the triangle
/*!
 @return The V axis of the triangle
 */
template <class T>
Vector3d<T> Trgl3dFast<T>::v_axis() const {
	return Vector3d<T>(p1_, p3_);
}

//! Compute the normal of the triangle
/*!
 @return The normal of the triangle
 @note This function returns the null vector if the triangle is degenerate
 */
template <class T>
Vector3d<T> Trgl3dFast<T>::normal() const {
	return Vector3d<T>(p1_, p2_).cross( Vector3d<T>(p1_, p3_) ).normalize();
}

//! Compute the point along the triangle at given parametric coordinates
/*!
 @param coords The parametric coordinates along the triangle
 @return The point along the triangle at given parametric coordinates
 */
template <class T>
Point3d<T> Trgl3dFast<T>::point(const Coords2d<T>& coords) const {
	GAMEENGINE_IMPLEMENT_TRGL3D_PARAMETRIC_POINT(p1_,p2_,p3_,coords,T)
}

//! Compute the parametric coordinates of given point along the triangle
/*!
 *  If given point does not belong to the plane of the triangle, the coordinates
 *  of its orthogonal projection along the plane are returned.
 *
 *  Each time this function is called, a transformer has to be created. If you want
 *  to convert multiple points to parametric coordinates, it is much more efficient
 *  to directly access the transformer and use the transform() function.
 *
 @param pt The point whose parametric coordinates should be computed
 @return The parametric coordinates of the given point
 */
template <class T>
Coords2d<T> Trgl3dFast<T>::coordinates(const Point3d<T>& pt) const {
	GAMEENGINE_IMPLEMENT_TRGL3D_PARAMETRIC_COORDS(pt,T)
}

//! Build a transformer that converts points in the Cartesian space to parametric coordinates
/*!
 @return A transformer that converts points in the Cartesian space to parametric coordinates
 */
template <class T>
Transformer3d<T> Trgl3dFast<T>::transformer() const {
	GAMEENGINE_IMPLEMENT_TRGL3D_PARAMETRIC_TRANSFORMER(p1_,p2_,p3_,T)
}

//! Accessor to the given point (read only)
/*!
 @return A constant reference to the given point
 */
template <class T>
const Point3d<T>& Trgl3dFast<T>::operator[](Trgl3D::Index index) const {
	if ( index == Trgl3D::First ) return p1_;
	if ( index == Trgl3D::Second ) return p2_;
	return p3_;
}

//! Test wether a point is inside the triangle plane
/*!
 @param rhs The point to test for coplanarity
 @param tolerance It is unlikely that the point falls exactly on the
                  triangle plane. The tolerance parameter allows for
				  approximate coplanarity
 @return True if the point is inside the triangle plane, false otherwise
 */
template <class T>
bool Trgl3dFast<T>::coplanar(const Point3d<T>& rhs, const T& tolerance) const {
	GAMEENGINE_IMPLEMENT_TRGL3D_COPLANAR(p1_, rhs, tolerance, T)
}

//! Test wether a point is inside the triangle
/*!
 @param rhs The point to test for inclusion
 @param strict If set to true, a point located exactly on the perimeter of
			   the triangle is not considered to belong to the triangle
 @return True if the point is inside the triangle, false otherwise
 */
template <class T>
bool Trgl3dFast<T>::inside(const Point3d<T>& rhs, bool strict) const {
	GAMEENGINE_IMPLEMENT_TRGL3D_INSIDE(p1_, p2_, p3_, rhs, strict, T)
}


//! Compute the intersection between the line and the triangle
/*!
 @param rhs The line to intersect with the triangle
 @param line_coordinate The parametric line coordinate of the intersection between
                        the triangle and the line, if both do intersect. To retrieve
					    the intersection point, call rhs.point(line_coordinate)
 @param coordinates The triangle parametric coordinates of the intersection
                    between the triangle and the line, if both do intersect. To
					retrieve the intersection point, call point(coordinates)
 @param type If not nil, this parameter receives the type of intersection
             between the triangle and the line. The intersection type can
			 be:<BR>
			 NoIntersection if the line does not intersect the triangle,<BR>
			 ExternIntersection if intersection occurs out of the triangle,<BR>
			 EdgeIntersection if intersection occurs on one of the edges of
			 the triangle,<BR>
			 Parallel if the line is parallel to the triangle,<BR>
			 Intersection if the line intersects the triangle
 @return True if the line intersects the triangle, false otherwise
 @note Type T should have an implicit conversion to type double
 */
template <class T>
bool Trgl3dFast<T>::intersection(const Line3d<T>& rhs, T& line_coordinate, Coords2d<T>& coordinates, IntersectionType::Type* type) const {
	GAMEENGINE_IMPLEMENT_TRGL3D_INTERSECTION_LINE(p1_, p2_, p3_, rhs, line_coordinate, coordinates, type, T)
}

//! Compute the parametric coordinates of the closest point along the triangle
/*!
 @param rhs The point to project on the triangle
 @param coords Receives the parametric coordinates of the closest point along the triangle
 @return True if parametric coordinates of the closest point could be computed (this function
         should return false only if the triangle is degenerate)
 @note If you want to get back to the point location, call the point(coords) function
 */
template <class T>
bool Trgl3dFast<T>::closest_point(const Point3d<T>& rhs, Coords2d<T>& coords) const {
	GAMEENGINE_IMPLEMENT_TRGL3D_CLOSEST_POINT(p1_, p2_, p3_, rhs, coords, T)
}

//! Compute the closest point along the triangle
/*!
 @return The closest point along the triangle
 @param rhs The point to project on the triangle
 @note This is an overloaded function, provided for convenience and for compatibility
       with some template classes. It is slower than the function
	   closest_point(const Point3d<T>& rhs, Coords2d<T>& coords). Unless you explicitly
	   need the point location, its use is therefore not advised
 */
template <class T>
Point3d<T> Trgl3dFast<T>::closest_point(const Point3d<T>& rhs) const {
	// Get the point coordinates
	Coords2d<T> coordinates;
	closest_point(rhs, coordinates);

	// Return the point location
	return point(coordinates);
}

//! Compute the perimeter of the triangle
/*!
 @return The perimeter of the triangle
 */
template <class T>
T Trgl3dFast<T>::perimeter() const {
	GAMEENGINE_IMPLEMENT_TRGL3D_PERIMETER(p1_, p2_, p3_, T)
}

//! Compute the area of the triangle
/*!
 @return The area of the triangle
 */
template <class T>
T Trgl3dFast<T>::area() const {
	GAMEENGINE_IMPLEMENT_TRGL3D_AREA(p1_, p2_, p3_, T)
}

//! Check if the triangle is degenerate
/*!
 @param tolerance Allows for approximately degenerate triangles
 @return True if the triangle is degenerate, false otherwise
 @note The triangle is considered degenerate if its area is equal
       or below the tolerance threshold
 */
template <class T>
bool Trgl3dFast<T>::degenerate(const T& tolerance) const {
	return ( area() <= tolerance );
}

//! Check if the coordinates are in the triangle
/*!
 @param coords The parametric coordinates to test
 @param strict If set to true, points located on the edges of the triangle
               are not considered to belong to the triangle
 @return True if the coordinates are in the triangle, false otherwise
 */
template <class T>
bool Trgl3dFast<T>::in_trgl(const Coords2d<T>& coords, bool strict) {
	if ( strict ) return ( ( coords.u() >  0 ) && ( coords.v() >  0 ) && ( (coords.u()+coords.v()) <  1 ) );
	/* else */    return ( ( coords.u() >= 0 ) && ( coords.v() >= 0 ) && ( (coords.u()+coords.v()) <= 1 ) );
}

//! Compute the plane in which the triangle lies
/*!
 @return The plane in which the triangle lies
 */
template <class T>
Plane3d<T> Trgl3dFast<T>::plane() const {
	GAMEENGINE_IMPLEMENT_TRGL3D_PLANE(p1_, p2_, p3_, T)
}

#ifdef GAMEENGINE_NAMESPACES
} // namespace GameEngine
#endif

#endif
